#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "jnaexc.h"

void jnaex_free(void *pt) {
    if (pt) {
        free(pt);
        pt = NULL;
    }

    printf("[ c.jnaex_free ] cleaning up memory...\n");
}

int jnaex_double(int val) {
    return val * 2;
}

jnaex_chars_t jnaex_getchars() {
    jnaex_chars_t chars;

    chars.data = (char *)malloc(sizeof(char) * 6);
    memset(chars.data, 0, sizeof(char) * 6);
    strcpy(chars.data, "hello");

    return chars;
}

void jnaex_chars_free(jnaex_chars_t chars) {
     if (chars.data) {
        free(chars.data);
        chars.data = NULL;
    }

    printf("[ c.jnaex_chars_free ] cleaning up memory...\n");
}

void jnaex_sendstring(const char* in) {
    printf("[ c.jnaex_sendstring ] in: %s\n", in);
}

void jnaex_getstring(char** out) {
    *out = (char *)malloc(sizeof(char) * 6);
    memset(*out, 0, sizeof(char) * 6);
    strcpy(*out, "hello");
}

void jnaex_sendstrings(const jnaex_strings_t *jstr) {
    int idx = 0;

    printf("[ c.jnaex_sendstrings ] jstr.n: %d\n", jstr->n);
    for (idx = 0; idx < jstr->n; idx++) {
        printf("[ c.jnaex_sendstrings ] jstr.data: idx: %d, val: %s\n", idx, jstr->data[idx]);
    }
}

jnaex_strings_t jnaex_getstrings() {
    jnaex_strings_t jstr;
    jstr.n = 3;

    jstr.data = (char **)malloc(sizeof(char *) * jstr.n);

    jstr.data[0] = (char *)malloc(sizeof(char) * 4);
    memset(jstr.data[0], 0, sizeof(char) * 4);
    strcpy(jstr.data[0], "ten");

    jstr.data[1] = (char *)malloc(sizeof(char) * 7);
    memset(jstr.data[1], 0, sizeof(char) * 7);
    strcpy(jstr.data[1], "eleven");

    jstr.data[2] = (char *)malloc(sizeof(char) * 7);
    memset(jstr.data[2], 0, sizeof(char) * 7);
    strcpy(jstr.data[2], "twelve");

    return jstr;
}

void jnaex_cleanstrs(jnaex_strings_t jstr) {
    int idx = 0;

    for (idx = 0; idx < jstr.n; idx++) {
        free(jstr.data[idx]);
        jstr.data[idx] = NULL;
    }

    printf("[ c.jnaex_cleanstrs ] cleaning up memory, n: %d.\n", idx);
}


void jnaex_sendstrarrfast(const char *strarr, int n) {
    assert(NULL != strarr);

    int offset = 0, cnt = 0;
    char *pStr = NULL;

    printf("[ c.jnaex_sendstrarrfast ] n: %d, strarr: %p\n", n, strarr);
    while (cnt < n) {
        pStr = (char *)strarr + offset;
        printf("[ c.jnaex_sendstrarrfast ] idx: %d, offset: %d, str: %s.\n", cnt, offset, pStr);
        offset += strlen(pStr) + 1;
        cnt++;
    }
}

void jnaex_getstrarrfast(char **ppStrarr, int *n) {
    assert(NULL != ppStrarr);

    *n = 3;
    *ppStrarr = (char *)malloc(sizeof(char) * 14);
    memset(*ppStrarr, 0, sizeof(char) * 14);

    strcpy(*ppStrarr, "one");
    strcpy(*ppStrarr + 4, "two");
    strcpy(*ppStrarr + 8, "three");
}

void jnaex_sendstruct(const jnaex_data_t *data) {
    printf("[ c.jnaex_sendstruct ] data.v: %d\n", data->val);
}

jnaex_data_t jnaex_getstruct() {
    jnaex_data_t data = { .val = 9 };
    return data;
}

void jnaex_updatestruct(jnaex_data_t *data) {
    data->val *= 2;

}

int jnaex_countstruct(const jnaex_data_t *data, int n) {
    int idx = 0, total = 0;

    for (idx = 0; idx < n; idx++) {
        total += data[idx].val;
    }

    return total;
}

void jnaex_getstructarr(jnaex_data_t **ptData, int *size) {
    *size = 3;
    *ptData = (jnaex_data_t *)malloc(sizeof(jnaex_data_t) * 3);
    memset(*ptData, 0, sizeof(jnaex_data_t) * 3);

    (*ptData)[0].val = 1;
    (*ptData)[1].val = 2;
    (*ptData)[2].val = 3;
}

double jnaex_senddoublearr(const double *dv, int n) {
    int idx = 0;
    double total = 0;

    for (idx = 0; idx < n; idx++) {
        total += dv[idx];
    }

    return total;
}

void jnaex_getdoublearr(double **ptDouble, int *size) {
    int idx = 0;
    *size = 100;

    *ptDouble = (double *)malloc(sizeof(double) * *size);
    memset(*ptDouble, 0, sizeof(double) * *size);

    for (idx = 0; idx < *size; idx++) {
        (*ptDouble)[idx] = ((double)idx + 100) / 100;
    }
}

double jnaex_senddoublestruct(const jnaex_double_t *ds) {
    int idx = 0;
    double total = 0.0;

    assert(NULL != ds);

    if ( 0 == ds->n) return total;

    assert(NULL != ds->data);

    for (idx = 0; idx < ds->n; idx++) {
        total += ds->data[idx];
    }

    return total;
}

jnaex_double_t jnaex_getdoublestruct() {
    int idx = 0;
    jnaex_double_t ds = { .n = 100 };

    ds.data = (double *)malloc(sizeof(double) * ds.n);
    memset(ds.data, 0, sizeof(double) * ds.n);

    for (idx = 0; idx < ds.n; idx++) {
        ds.data[idx] = ((double)idx + 100) / 100;
    }

    return ds;
}

void jnaex_senddatalist(const jnaex_data_list_t *datalist) {
    int idx = 0;

    printf("[ c.jnaex_senddatalist ] n: %d, strarr: %p\n", datalist->n, datalist);

    for (idx = 0; idx < datalist->n; idx++) {
        printf("[ c.jnaex_senddatalist ] idx: %d, val: %d\n", idx, datalist->data[idx].val);
    }
}

jnaex_data_list_t *jnaex_getdatalist() {
    int idx = 0;
    jnaex_data_list_t *datalist = (jnaex_data_list_t *)malloc(sizeof(jnaex_data_list_t));

    datalist->n = 0;
    datalist->data = (jnaex_data_t *)malloc(sizeof(jnaex_data_t) * datalist->n);
    printf("[ c.jnaex_getdatalist ]  1111 data: %p\n", datalist->data);

    for (idx = 0; idx < datalist->n; idx++) {
        datalist->data[idx].val = idx * idx;
        datalist->data[idx].str = 0xf;
    }

    return datalist;
}

void jnaex_data_list_free(jnaex_data_list_t datalist) {
     if (datalist.data) {
        free(datalist.data);
        datalist.data = NULL;
    }

    printf("[ c.jnaex_data_list_free ] cleaning up memory...\n");
}

int jnaex_call_with_cb(const jnaex_callback jcallback, const char *session, void *userdata) {
    printf("[ c.jnaex_call_with_cb.pre  ] session: %s, userdata: %p\n", session, userdata);
    jcallback(session, userdata);
    printf("[ c.jnaex_call_with_cb.post ] session: %s, userdata: %p\n", session, userdata);

    return 0;
}
